#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2025/2/5 21:47
# @Author  : Ramsey
# @Site    : zh
# @File    : middleware_manager.py
# @Software: PyCharm
from pprint import pformat
from typing import Dict, List, Callable
from types import MethodType
from spider_framework.utils.log import spider_logger
from spider_framework.utils.project import load_class
from spider_framework.http.request import Request
from spider_framework.http.response import Response
from spider_framework.utils.error import MiddlewareInitError
from spider_framework.utils.transform import common_call


class MiddlewareManager:
    def __init__(self, crawler):
        self.crawler = crawler
        self.logger = spider_logger(self.__class__.__name__, log_level=self.crawler.settings.get("LOG_LEVEL"))
        self.middlewares: List = []
        self.methods: Dict[str, List[MethodType]] = {}
        middlewares = self.crawler.settings.get("MIDDLEWARES")
        self._add_middleware(middlewares)
        self._add_method()
        self.download_method: Callable = self.crawler.engine.downloader.download

    async def _process_request(self, request):
        for method in self.methods["process_request"]:
            # result = method(request, self.crawler.spider)
            result = await common_call(method, request, self.crawler.spider)
            if result is None:
                continue
            if isinstance(result, Request):
                self.crawler.engine._push_request(result)
            if isinstance(result, Response):
                return result

    async def download(self, request):
        await self._process_request(request)
        response = await self.download_method(request)
        return response

    def _add_method(self):
        for middleware in self.middlewares:
            if hasattr(middleware, "process_request"):
                self.methods.setdefault("process_request", []).append(getattr(middleware, "process_request"))
            if hasattr(middleware, "process_response"):
                self.methods.setdefault("process_response", []).append(getattr(middleware, "process_response"))
            if hasattr(middleware, "process_exception"):
                self.methods.setdefault("process_exception", []).append(getattr(middleware, "process_exception"))

    def _add_middleware(self, middlewares):
        enable_middlewares = [middleware for middleware in middlewares if self._validate_middleware(middleware)]
        if enable_middlewares:
            self.logger.info(f"Load middlewares: \n {pformat(enable_middlewares)}")

    def _validate_middleware(self, middleware):
        middleware_cls = load_class(middleware)
        if not hasattr(middleware_cls, "create_instance"):
            raise MiddlewareInitError(f"{middleware} must have a `create_instance` method")
        middleware_instance = middleware_cls.create_instance(self.crawler)
        self.middlewares.append(middleware_instance)
        return True

    @classmethod
    def create_instance(cls, *args, **kwargs):
        return cls(*args, **kwargs)


if __name__ == "__main__":
    pass
